<?php

// @formatter:off
/**
 * @file WCPreviews.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-06-03 03:40:32 +0000 (Tue, 03 Jun 2014) $
 *
 * $Id: WCPreviews.php 112 2014-06-03 03:40:32Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/includes/WCPreviews.php $
 */
// @formatter:on

/**
 * @class WCPreviewsManager @todo doc
 */
class WCPreviewsManager extends WCSingleton {
	//
	// Constants.
	const TypeCollection = "CL";
	const TypeComic = "CX";
	const TypeComicImage = "CI";
	const TypeDirectory = "DI";
	const TypeImage = "IM";
	const TypeRule = "RL";
	const TypeRuleAction = "RA";
	const TypeTag = "TA";
	const MaxPerCollection = 10;
	const MaxPerDirectory = 10;
	const MaxPerRule = 10;
	const MaxPerRuleAction = 10;
	const MaxPerTag = 10;
	//
	// Protected varaibles.
	/**
	 * @var WCConnection Database pointer shortcut.
	 */
	protected $_db;
	/**
	 * @var string Database tables' prefix shortcut.
	 */
	protected $_dbprefix;
	//
	// Public methods.
	public function addPreviews(WCGroup $group, $type, $id, $position, $path) {
		$out = false;

		if($type != self::TypeComic || $position == 0) {
			$stmt = false;
			$stmtID = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtID)) {
				$stmt = $this->_db->get($stmtID);
			} else {
				$query = "insert\n";
				$query.= "        into {$this->_dbprefix}previews(\n";
				$query.= "                pvw_group, pvw_type, pvw_id, pvw_position, pvw_path, pvw_lost)\n";
				$query.= "        values (:group, :type, :id, :position, :path, 'N')\n";
				$query.= "        on duplicate key\n";
				$query.= "                update  pvw_path = :path,\n";
				$query.= "                        pvw_lost = 'N'\n";
				$stmt = $this->_db->prepare($stmtID, $query);
			}

			$params = array(
				":group" => $group->id,
				":type" => $type,
				":id" => $id,
				":position" => $position,
				":path" => $path
			);

			if($stmt->execute($params)) {
				$out = true;
			}
		}

		return $out;
	}
	public function checkAllPaths(WCGroup $group) {
		$invalids = array();

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   pvw_group = :group\n";
			$query.= " and    pvw_lost  = 'N'\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $group->id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			foreach($stmt->fetchAll() as $row) {
				if(!$this->checkPath($group, $row["pvw_path"])) {
					$invalids[] = $row["pvw_path"];
				}
			}
		}

		$invalids = array_unique($invalids);

		return $invalids;
	}
	public function checkPath(WCGroup $group, $path) {
		$out = true;

		global $wcPaths;

		$fullPath = WCSanitizer::SystemPath("{$wcPaths["cache-directory"]}/{$wcPaths["cache-thumbs-dirname"]}/{$path}");
		if(!is_readable($fullPath)) {
			$this->invalidatePath($group, $path);
			$out = false;
		}

		return $out;
	}
	public function createCollectionPreviews(WCCollection $collection) {
		$out = false;

		$this->resetPreviews($collection->group(), self::TypeCollection, $collection->id());

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   (pvw_type,pvw_id) in (\n";
			$query.= "                select  '".self::TypeImage."',\n";
			$query.= "                        img_id\n";
			$query.= "                from    {$this->_dbprefix}images\n";
			$query.= "                where   img_collection = :collection\n";
			$query.= "                 and    img_lost       = 'N'\n";
			$query.= "        union all\n";
			$query.= "                select  '".self::TypeComic."',\n";
			$query.= "                        cmc_id\n";
			$query.= "                from    {$this->_dbprefix}comics\n";
			$query.= "                where   cmc_collection = :collection\n";
			$query.= "                 and    cmc_lost       = 'N'\n";
			$query.= "        union all\n";
			$query.= "                select  '".self::TypeCollection."',\n";
			$query.= "                        col_id\n";
			$query.= "                from    {$this->_dbprefix}collections\n";
			$query.= "                where   col_parent = :collection\n";
			$query.= "        )\n";
			$query.= " and    pvw_group = :group\n";
			$query.= " and    pvw_lost  = 'N'\n";
			$query.= "order by rand()\n";
			$query.= "limit 100\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $collection->group,
			":collection" => $collection->id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			$position = 0;
			foreach($stmt->fetchAll() as $row) {
				if($position < self::MaxPerCollection) {
					if($this->addPreviews($collection->group(), self::TypeCollection, $collection->id(), $position, $row["pvw_path"])) {
						$position++;
					}
				} else {
					break;
				}
			}
			$out = true;
		}

		return $out;
	}
	public function createComicPreviews(WCComic $comic) {
		$out = false;

		$this->resetPreviews($comic->group(), self::TypeComic, $comic->id());

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   (pvw_type,pvw_id) in (\n";
			$query.= "                select  '".self::TypeComicImage."',\n";
			$query.= "                        cim_id\n";
			$query.= "                from    {$this->_dbprefix}comic_images\n";
			$query.= "                where   cim_comic = :comic\n";
			$query.= "                 and    cim_lost  = 'N'\n";
			$query.= "        )\n";
			$query.= " and    pvw_group = :group\n";
			$query.= " and    pvw_lost  = 'N'\n";
			$query.= "order by pvw_position asc\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $comic->group,
			":comic" => $comic->id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			$row = $stmt->fetch();
			$out = $this->addPreviews($comic->group(), self::TypeComic, $comic->id(), 0, $row["pvw_path"]);
		}

		return $out;
	}
	public function createDirectoryPreviews(WCDirectory $directory) {
		$out = false;

		$this->resetPreviews($directory->group(), self::TypeDirectory, $directory->id());

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   (pvw_type,pvw_id) in (\n";
			$query.= "                select  '".self::TypeImage."',\n";
			$query.= "                        img_id\n";
			$query.= "                from    {$this->_dbprefix}images\n";
			$query.= "                where   img_directory = :directory\n";
			$query.= "                 and    img_lost      = 'N'\n";
			$query.= "        union all\n";
			$query.= "                select  '".self::TypeComic."',\n";
			$query.= "                        cmc_id\n";
			$query.= "                from    {$this->_dbprefix}comics\n";
			$query.= "                where   cmc_directory = :directory\n";
			$query.= "                 and    cmc_lost      = 'N'\n";
			$query.= "        )\n";
			$query.= " and    pvw_group = :group\n";
			$query.= " and    pvw_lost  = 'N'\n";
			$query.= "order by rand()\n";
			$query.= "limit 100\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $directory->group,
			":directory" => $directory->id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			$position = 0;
			foreach($stmt->fetchAll() as $row) {
				if($position < self::MaxPerDirectory) {
					$this->addPreviews($directory->group(), self::TypeDirectory, $directory->id(), $position, $row["pvw_path"]);
					$position++;
				} else {
					break;
				}
			}
			$out = true;
		}

		return $out;
	}
	public function createPreview(WCGroup $group, $type, $id, $position, $path, $source) {
		$out = false;

		//
		// Loading global path settings.
		global $wcPaths;
		global $wcDefaults;
		//
		// Checking that it is possible to read the file.
		if(is_readable($source)) {
			$data = array_merge(getimagesize($source), pathinfo($source));
			$data["width"] = $data[0];
			$data["height"] = $data[1];
			$fullPath = WCSanitizer::SystemPath("{$wcPaths["cache-directory"]}/{$wcPaths["cache-thumbs-dirname"]}/{$path}");


			//
			// Enforcing the existence of previews directory.
			if(!is_dir(dirname($fullPath))) {
				//
				// Physically creating the directory.
				mkdir(dirname($fullPath), $wcDefaults["directory-permissions"], true);
			}
			@unlink($fullPath);

			$img = null;
			switch($data["mime"]) {
				case "image/jpeg":
					$img = @imagecreatefromjpeg($source);
					break;
				case "image/png":
					$img = @imagecreatefrompng($source);
					break;
				case "image/gif":
					$img = @imagecreatefromgif($source);
					break;
				default:
					break;
			}
			//
			// Checking if the image was successfully opened.
			if($img) {
				$w = $wcDefaults["thumbs-size"];
				$h = $wcDefaults["thumbs-size"];
				if($data["width"] > $data["height"]) {
					$h = round($data["height"] * $wcDefaults["thumbs-size"] / $data["width"]);
				} else {
					$w = ($data["width"] * $wcDefaults["thumbs-size"] / $data["height"]);
				}

				$previewImg = imagecreatetruecolor($w, $h);
				imagecopyresampled($previewImg, $img, 0, 0, 0, 0, $w, $h, $data["width"], $data["height"]);

				switch($data["mime"]) {
					case "image/jpeg":
						imagejpeg($previewImg, $fullPath);
						$out = true;
						break;
					case "image/png":
						imagepng($previewImg, $fullPath);
						$out = true;
						break;
					case "image/gif":
						imagegif($previewImg, $fullPath);
						$out = true;
						break;
					default:
						break;
				}

				imagedestroy($img);
				imagedestroy($previewImg);
			}
		}
		if($out) {
			$this->addPreviews($group, $type, $id, $position, $path);
		}

		return $out;
	}
	public function createRulePreviews(WCRule $rule) {
		$out = false;

		$this->resetPreviews($rule->group(), self::TypeRule, $rule->id());

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   (pvw_type,pvw_id) in (\n";
			$query.= "                select  '".self::TypeRuleAction."',\n";
			$query.= "                        rel_ra_action\n";
			$query.= "                from    {$this->_dbprefix}rule_actions\n";
			$query.= "                where   rel_ra_rule = :rule\n";
			$query.= "        )\n";
			$query.= " and    pvw_group = :group\n";
			$query.= " and    pvw_lost  = 'N'\n";
			$query.= "order by rand()\n";
			$query.= "limit 100\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $rule->group,
			":rule" => $rule->id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			$position = 0;
			foreach($stmt->fetchAll() as $row) {
				if($position < self::MaxPerRule) {
					$this->addPreviews($rule->group(), self::TypeRule, $rule->id(), $position, $row["pvw_path"]);
					$position++;
				} else {
					break;
				}
			}
			$out = true;
		}

		return $out;
	}
	public function createTagPreviews(WCTag $tag) {
		$out = false;

		$this->resetPreviews($tag->group(), self::TypeTag, $tag->id());

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   (pvw_type,pvw_id) in (\n";
			$query.= "                select  '".self::TypeImage."',\n";
			$query.= "                        mem_image\n";
			$query.= "                from    {$this->_dbprefix}tag_images\n";
			$query.= "                where   mem_tag = :tag\n";
			$query.= "        union all\n";
			$query.= "                select  '".self::TypeComic."',\n";
			$query.= "                        mem_comic\n";
			$query.= "                from    {$this->_dbprefix}tag_comics\n";
			$query.= "                where   mem_tag = :tag\n";
			$query.= "        )\n";
			$query.= " and    pvw_group = :group\n";
			$query.= " and    pvw_lost  = 'N'\n";
			$query.= "order by rand()\n";
			$query.= "limit 100\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $tag->group,
			":tag" => $tag->id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			$position = 0;
			foreach($stmt->fetchAll() as $row) {
				if($position < self::MaxPerTag) {
					$this->addPreviews($tag->group(), self::TypeTag, $tag->id(), $position, $row["pvw_path"]);
					$position++;
				} else {
					break;
				}
			}
			$out = true;
		}

		return $out;
	}
	public function getPreviews(WCGroup $group, $type, $id) {
		$out = array();

		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}previews\n";
			$query.= "where   pvw_group = :group\n";
			$query.= " and    pvw_type  = :type\n";
			$query.= " and    pvw_id    = :id\n";
			$query.= " and    pvw_lost  = 'N'\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $group->id,
			":type" => $type,
			":id" => $id
		);

		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			//
			// Loading global path settings.
			global $wcPaths;

			foreach($stmt->fetchAll() as $row) {
				if($this->checkPath($group, $row["pvw_path"])) {
					$out[(int) $row["pvw_position"]] = $row["pvw_path"];
				}
			}
		}

		return $out;
	}
	public function resetPreviews(WCGroup $group, $type, $id) {
		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "delete from {$this->_dbprefix}previews\n";
			$query.= "where       pvw_group = :group\n";
			$query.= " and        pvw_type  = :type\n";
			$query.= " and        pvw_id    = :id\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $group->id,
			":type" => $type,
			":id" => $id
		);

		$stmt->execute($params);
	}
	//
	// Protected methods.
	protected function init() {
		parent::init();
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $this->_db->prefix();
		// @}
	}
	protected function invalidatePath(WCGroup $group, $path) {
		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "update  {$this->_dbprefix}previews\n";
			$query.= "set     pvw_lost = 'Y'\n";
			$query.= "where   pvw_group = :group\n";
			$query.= " and    pvw_path  = :path\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $group->id,
			":path" => $path
		);

		$stmt->execute($params);
	}
}
